package com.pclong.fightlandlordserver;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.pclong.fightlandlordserver.F3ServerProxy.LogType;
import com.pclong.fightlandlordserver.command.handler.F3ServerInMessageHandler;
import com.pclong.fightlandlordserver.command.handler.PlatformInMessageHandler;
import com.pclong.fightlandlordserver.command.handler.PlayerInMessageHandler;
import com.pclong.fightlandlordserver.command.handler.FightLandlordGameInMessageHandler;
import com.pclong.fightlandlordserver.command.message.EchoMessage;
import com.pclong.fightlandlordserver.command.message.F3ServerMessage;
import com.pclong.fightlandlordserver.command.message.PlatformMessage;
import com.pclong.fightlandlordserver.command.message.PlayerMessage;
import com.pclong.fightlandlordserver.command.message.F3ServerMessage.MessageType;
import com.pclong.fightlandlordserver.command.message.game.FightLandlordGameMessage;
import com.pclong.fightlandlordserver.command.message.game.PushdownWinGameMessage;
import com.pclong.fightlandlordserver.command.message.game.QiongWinGameMessage;
import com.pclong.fightlandlordserver.command.message.game.Red5GameMessage;
import com.pclong.fightlandlordserver.data.HibernateSessionFactory;
import com.pclong.fightlandlordserver.data.metadata.LogInfo;
import com.pclong.fightlandlordserver.game.Game;
import com.pclong.fightlandlordserver.game.GamePool;
import com.pclong.fightlandlordserver.game.model.FightLandlordGame;
import com.pclong.fightlandlordserver.game.model.GameStatus;
import com.pclong.fightlandlordserver.game.model.Platform;
import com.pclong.fightlandlordserver.game.model.Player;
import com.pclong.fightlandlordserver.game.model.Room;
import com.pclong.fightlandlordserver.util.EncryptionUtil;
import com.pclong.fightlandlordserver.util.HandlerDispatcher;
import com.pclong.fightlandlordserver.util.ModelUtil;
import com.pclong.fightlandlordserver.util.StringHelper;
import com.pclong.fightlandlordserver.util.SystemLogger;

import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.core.session.IoSessionConfig;
import org.apache.mina.handler.demux.DemuxingIoHandler;
import org.apache.mina.handler.demux.MessageHandler;
import org.apache.mina.transport.socket.SocketSessionConfig;

/**
 *
 */
public class F3ServerServiceHandler extends DemuxingIoHandler {

    private static final Log log = LogFactory.getLog(F3ServerServiceHandler.class);
    Base64 base64 = new Base64();
    // TODO What's the differences between sessions and managed-sessions of mina ?
    // In the example of mina a third sessions is used for session management. 
    private final Set<IoSession> sessions = Collections.synchronizedSet(new HashSet<IoSession>());
    private ExecutorService idleFutureExecutor = Executors.newFixedThreadPool(new Integer(ModelUtil.getSystemParameter("MAX_THREADS_IN_IDLE_FUTURE_EXECUTOR", 100)));
//    private static final  Gson gson = new GsonBuilder().setPrettyPrinting().create();

    /**
     * @param platform
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    public F3ServerServiceHandler(Platform platform) throws InstantiationException, IllegalAccessException {
        ModelUtil.setSessions(sessions);
        this.addReceivedMessageHandler(PlatformMessage.class, new PlatformInMessageHandler());
        this.addReceivedMessageHandler(PlayerMessage.class, new PlayerInMessageHandler());
        this.addReceivedMessageHandler(FightLandlordGameMessage.class, new FightLandlordGameInMessageHandler());
        Iterator<MessageHandler<?>> itr = this.getReceivedMessageHandlerMap().values().iterator();
        while (itr.hasNext()) {
            F3ServerInMessageHandler handler = (F3ServerInMessageHandler) itr.next();
            HandlerDispatcher.initMethodPoolForHandler(handler);
        }
    }

    @Override
    public void sessionCreated(IoSession session) {
        // 默认90分钟后自动超时
//        int idleTime = new Integer(ModelUtil.getSystemParameter("IDLE_TIME", 30));
//        session.getConfig().setIdleTime(IdleStatus.BOTH_IDLE, idleTime);
        IoSessionConfig cfg = session.getConfig();
        if (cfg instanceof SocketSessionConfig) {
            ((SocketSessionConfig) cfg).setReadBufferSize(2048);
            ((SocketSessionConfig) cfg).setKeepAlive(true);
            ((SocketSessionConfig) cfg).setSoLinger(0);
//    ((SocketSessionConfig) cfg).setSoLinger(true, 0);
            ((SocketSessionConfig) cfg).setTcpNoDelay(true);
            ((SocketSessionConfig) cfg).setWriteTimeout(1000 * 5);
        }
        // 添加Session
        sessions.add(session);
    }

    @Override
    public void sessionOpened(IoSession session) throws Exception {
        super.sessionOpened(session);
    }

    @Override
    public void sessionIdle(IoSession session, IdleStatus idlestatus) throws Exception {
        // 到达空闲时间时，主动询问客户端
        EchoMessage echoMessage = F3ServerMessage.createInstance(MessageType.PLATFORM).getEchoMessage();
        echoMessage.setResult(PlatformInMessageHandler.SERVER_IDLE_TEST);
        session.setAttribute("ALIVE", null);
        sessionWrite(session, echoMessage);
        final IoSession threadSession = session;
        idleFutureExecutor.execute(new Runnable() {

            public void run() {
                try {
                    Thread.sleep(15 * 1000);
                } catch (Exception e) {
                }
                if (!"yes".equals(threadSession.getAttribute("ALIVE"))) {
                    threadSession.close(true);
                }
            }
        });
        // session.setAttributeIfAbsent(obj, obj1);
        // 关闭Session
        // iosession.close(true);
    }

    @Override
    public void sessionClosed(IoSession session) throws Exception {
        // 移除用户登录信息
        sessions.remove(session);
        Player player = ModelUtil.getPlayer(session);
        if (player == null) {
            return;
        }
        Room room = ModelUtil.getPlatform().getChild("1").getChild(player.getRoomid());
        // 清除与游戏相关的内容
        Game<?> game = GamePool.getGame(player.getGameId(), FightLandlordGame.class);
        if (game != null) {
            synchronized (game) {
                for (Player eachPlayer : game.getPlayers()) {
                    // 通知游戏中的其他玩家游戏已经中断，如果想重新加入游戏，必须进入游戏队列中
                    if (!session.equals(eachPlayer.getIosession())) {
                        eachPlayer.setCurrentStatus(GameStatus.IDLE);
                        eachPlayer.setState(2);
                        EchoMessage echoMessage = F3ServerMessage.createInstance(MessageType.FIGHT_LANDLORD).getEchoMessage(); // FIXME THIS SHOULD BE CHANGE TO PLATFORM EVENT TYPE
                        echoMessage.setResult("GAME_INTERRUPTED_OTHER_DISCONNECT");
                        String json = "{\"currentpos\":" + player.getPos() + ",\"lastpost\":" + player.getPos() + "}";
                        echoMessage.setContent(json);
                        sessionWrite(eachPlayer.getIosession(), echoMessage);
                        log.info("游戏中玩家发送通知:" + eachPlayer.getUserId() + ",有玩家断线：" + player.getUserId());
                    }
                }
                // 计算掉线积分
                game.persistDisconnectScore(player);
            }
            GamePool.distroyGame(game.getId(), game.getClass());
        } else if (room != null) {
            Player roomplayer;
            Set<String> players = room.getChildren().keySet();
            EchoMessage echoMessage = F3ServerMessage.createInstance(MessageType.FIGHT_LANDLORD).getEchoMessage();
            echoMessage.setContent("{\"currentpos\":" + player.getPos() + ",\"lastpost\":" + player.getPos() + "}");
            echoMessage.setResult("GAME_INTERRUPTED_OTHER_DISCONNECT");
            for (String p : players) {
                roomplayer = room.getChildren().get(p);
                //如果不是当前用户，则通知有玩家加入                    
                if (!p.equals(String.valueOf(player.getPos()))) {
                    sessionWrite(roomplayer.getIosession(), echoMessage);
                    log.info("本房间玩家发送通知:" + roomplayer.getUserId() + "," + "GAME_INTERRUPTED_OTHER_DISCONNECT" + ",json:" + echoMessage.getContent());
                }
            }
        }

        //通知其他在线玩家，大厅的玩家，更新在线列表，如果当前玩家加入房间，则更新房间显示
        synchronized (sessions) {
            Iterator<IoSession> itr = sessions.iterator();
            while (itr.hasNext()) {
                IoSession currentSession = itr.next();
                if (currentSession.equals(session)) {
                    continue;
                }
                Player loginplayer = (Player) currentSession.getAttribute(Player.ATTR_NAME);
                //大厅的玩家发通知
                if (loginplayer != null && loginplayer.getState() == 1) {
                    // 用户已掉线
                    EchoMessage echoMessage = F3ServerMessage.createInstance(MessageType.FIGHT_LANDLORD).getEchoMessage();
                    echoMessage.setResult("LOBBY_USER_LOG_OUT");
                    echoMessage.setContent(GetPlayerInfoByJson(player));
                    sessionWrite(loginplayer.getIosession(), echoMessage);
                    log.info("大厅玩家掉线:" + loginplayer.getUserId() + ",有玩家掉线：" + player.getUserId());

                }
            }
        }
        if (player.getParent() != null) {
            ModelUtil.getPlayer(session).getParent().removeChild(String.valueOf(player.getPos()));
            //ModelUtil.getPlayer(session).setParent(null);
            log.debug("从房中移除玩家：room:" + room.getName() + ",player:" + player.getUserId());
        }
//        HibernateSessionFactory.getSession().save(SystemLogger.createLog("SESSION CLOSED",
//                session.getRemoteAddress().toString(),
//                player.getId(),
//                LogType.SYSTEM_LOG));
//        HibernateSessionFactory.getSession().flush();
//        HibernateSessionFactory.getSession().close();
        super.sessionClosed(session);
    }

    private String GetPlayerInfoByJson(Player roomplayer) {
        if (roomplayer == null) {
            return "{}";
        }
        String json = "{\"pid\":\"" + roomplayer.getId() + "\",\"pos\":" + roomplayer.getPos() + ",\"name\":\"" + roomplayer.getName() + "\"";
        json += ",\"rid\":" + roomplayer.getRoomid();
        json += ",\"pos\":" + roomplayer.getPos();
        json += ",\"state\":" + roomplayer.getState();
        json += ",\"score\":" + roomplayer.getScore();
        json += ",\"money\":" + roomplayer.getMoney();
        json += ",\"avatar\":\"" + roomplayer.getAvatar() + "\"";
        json += ",\"ready\":" + roomplayer.isReady();
        json += "}";
        return json;
    }

    @Override
    public void messageReceived(IoSession session, Object message) throws Exception {
        // Flex安全策略
        if (String.valueOf(message).trim().equalsIgnoreCase("<policy-file-request/>")) {
            session.write(F3Server.SECURITY_CONFIGURATION);
            // FIXME THE FOLLOWING LINE MAY BE REMOVED SO THAT ONLY ONE CONNECTION WILL FETCH INFO FROM SOCKET SERVER.
            // session.close(true);
            return;
        }
        // 解析消息，消息构成格式为：消息类型、消息编号、消息签名、消息内容
        //3~1~GAME_CHEAT_FOUND~hello  0~0~SERVER_IDLE_TEST~null PLATFORM:0 PLAYER:1RED5GAME:2FIGHT_LANDLORD:3PUSHDOWN_WIN:4 QIONG_WIN:5
        //PlatformMessage.class, new PlatformInMessageHandler());
//        PlayerMessage,  PlayerInMessageHandler());登陆1~1~LOGIN_SIGN_IN~long~long.
//        FightLandlordGameMessage,  FightLandlordGameInMessageHandler()    
//使用Base64进行字串编码
//        String encodeString = new String(base64.encode("This is source string.".getBytes()));
////输出结果将为"VGhpcyBpcyBzb3VyY2Ugc3RyaW5nLg=="
//        System.out.println(encodeString);
////使用Base64进行字串解码
//        String decodeString = new String(base64.decode(encodeString.getBytes()));
////输出结果将为"This is source string."
//        System.out.println(decodeString);
//        message = EncryptionUtil.Base64Decode(message.toString());
        message=new String(base64.decode(message.toString().getBytes()));
        log.info("收到消息:" + message);
        String[] results = message.toString().split("~", 4);
        int msgType = Integer.parseInt(results[0]);
        long number = Long.parseLong(results[1]);
        String signature = results[2];
        String content = results[3];
        if (content != null) {
            if (content.length() == 0 || "NULL".equalsIgnoreCase(content)) {
                content = null;
            }
        }
        // 构造消息
        F3ServerMessage serverMessage = F3ServerMessage.createInstance(msgType);
        serverMessage.setType(msgType);
        serverMessage.setNumber(number);
        serverMessage.setSignature(signature);
        serverMessage.setContent(content);
        serverMessage.setCurrentSession(session);
        // serverMessage.setSessions(sessions);
        // 派发消息
        super.messageReceived(session, serverMessage);
    }

    @Override
    public void messageSent(IoSession session, Object message) throws Exception {
    }

    @Override
    public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
        try {
            log.warn("Global exception is found.");
            if (cause instanceof IOException) {
                // FIXME 原始IOException可能已经被RuntimeException包装，判断条件可能不合适
                log.warn("Try to close socket session and persist current game scores!");
                sessionClosed(session);
                session.close(true);
            }
            // 日志记录
            //LogInfo logInfo = SystemLogger.createLog(LogType.SYSTEM_ERROR.toString(), cause.getMessage(), StringHelper.convertExceptionStack2String(cause), LogType.SYSTEM_ERROR);
            //HibernateSessionFactory.getSession().save(logInfo);
            //HibernateSessionFactory.closeSession();
        } catch (Exception e) {
//            e.printStackTrace();
        }
    }

    /**
     * @param session
     * @param echoMessage
     */
    private void sessionWrite(IoSession session, EchoMessage echoMessage) {
//         session.write(gson.toJson(echoMessage));
        session.write(EncryptionUtil.Base64Encode(HandlerDispatcher.respondMessage(echoMessage)));
    }
}
